/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年9月5日
 */
package com.massyframework.beanskin.license;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.massyframework.beanskin.service.crypto.RSAService;

/**
 * 许可签发人
 * @author huangkh
 *
 */
public final class LicenseSigner {
	
	/**
	 * 
	 */
	public LicenseSigner() {
		
	}
	
	/**
	 * 签发许可
	 * @param builder {@link License.Builder},许可构建器
	 * @param privateKey {@link String},签名私钥
	 * @param x509Cert {@link String}, x509证书
	 * @param rasService {@lihk RASService}, 签名的证书
	 * @return {@link License}, 许可
	 * @throws IOException 
	 * @throws SignatureException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidKeySpecException 
	 * @throws InvalidKeyException 
	 * @throws NoSuchProviderException 
	 * @throws CertificateException 
	 */
	public License sign(License.Builder builder, String privateKey, String x509Cert, RSAService rasService) 
			throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, SignatureException, 
				IOException, CertificateException, NoSuchProviderException {
		this.checkLicenseBuilder(builder);
		Objects.requireNonNull(privateKey, "\"privateKey\" cannot be null.");
		Objects.requireNonNull(x509Cert, "\"x509Cert\" cannot be null.");
		Objects.requireNonNull(rasService, "\"rasService\" cannot be null.");
		
		String content = this.genericContent(builder);
		
		String signedData = rasService.sign(content, privateKey);
		if (rasService.verify(content, signedData, x509Cert)) {
			builder.signature(signedData)
				.x509cert(x509Cert);			
			return builder.build();
		}
		return null;
	}
		
	/**
	 * 许可验证
	 * @param license {@link License},许可
	 * @param rasService {@link RASService}, ras服务
	 * @return {@link boolean},返回{@code true}表示验证通过，否则返回{@code false}
	 * @throws InvalidKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws SignatureException
	 * @throws CertificateException
	 * @throws NoSuchProviderException
	 * @throws IOException 
	 * @throws InvalidKeySpecException 
	 */
	public boolean verifyWithX509(License license, RSAService rasService) 
			throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, 
			CertificateException, NoSuchProviderException, InvalidKeySpecException, IOException {
		Objects.requireNonNull(license, "\"license\" cannot be null.");
		Objects.requireNonNull(rasService, "\"rasService\" cannot be null.");
		
		String content = license.toContent();
		String x509cert = license.getX509Cert();
		return rasService.verify(content, license.getSignature(), x509cert);
	}
	
		
	/**
	 * 构建许可内容 
	 * @param builder {@link License.Builder}
	 * @return {@link String}
	 */
	protected String genericContent(License.Builder builder) {
		StringBuilder sb = new StringBuilder();
		sb.append("module=")
			.append(builder.groupId).append(":")
			.append(":").append(builder.moduleId)
			.append(":").append(builder.versionRange.toString())
			.append(";").append("\r\n");
		sb.append("customerId=")
			.append(builder.customerId.toString())
			.append(";").append("\r\n");
		
		if (builder.expireDate != null) {
			sb.append("expiredDate=")
				.append(builder.expireDate.toString())
				.append(";").append("\r\n");
		}
		
		Map<String, String> map = builder.params == null ?
				Collections.emptyMap() :
					builder.params;
		List<String> names = new ArrayList<>(map.keySet());
		if (!names.isEmpty()) {
			sb.append("[").append("\r\n");
			Collections.sort(names);
			int size = names.size();
			for (int i=0; i<size; i++) {
				String name = names.get(i);
				String value = map.get(name);
				sb.append(name).append("=").append(value);
				if (i != size -1) {
					sb.append(";");
				}
				sb.append("\r\n");
			}
			sb.append("]");
		}
		
		return sb.toString();
	}
	
	/**
	 * 检查许可构建器
	 * @param builder {@link License.Builder}
	 */
	protected void checkLicenseBuilder(License.Builder builder) {
		Objects.requireNonNull(builder.groupId, "\"groupId\" cannot be null.");
		Objects.requireNonNull(builder.moduleId , "\"moduleId\" cannot be null.");
		Objects.requireNonNull(builder.versionRange, "\"versionRange\" cannot be null.");
		Objects.requireNonNull(builder.customerId, "\"customerId\" cannot be null.");
	}

}
